package service

import (
	"context"
	"encoding/json"
	"fmt"
	"github.com/go-redis/redis/v8"
	"gitlab.vrviu.com/inviu/backend-go-public/gopublic"
	"gitlab.vrviu.com/inviu/backend-go-public/gopublic/rediswrap"
	"gitlab.vrviu.com/inviu/backend-go-public/gopublic/vlog"
	"strconv"
	"sync"
	"time"
)

func CreateAreaClientMgr(user_id_prefix string, start_uuid, gid uint64, biz_type uint64, uservo, uaccess, task_id string, count, areaType, holdtime, relaunch_duration int, wg *sync.WaitGroup, controller *ClientController) *AreaClientMgr {

	clientMgr := &AreaClientMgr{
		AreaClients: make([]*AreaClient, count),
		Result_key:  fmt.Sprintf("%s_performance_temp_flow", task_id),
	}

	for i := 0; i < count; i++ {
		var new_user_id string
		if controller.IsAutoRegister == false {
			new_id := start_uuid + uint64(i)
			new_user_id = strconv.FormatUint(new_id, 10)

		} else {
			new_user_id = fmt.Sprintf("%s-%s", user_id_prefix, gopublic.GenerateRandonString(12))
		}

		clientMgr.AreaClients[i] = CreateAreaClient(0, gid, biz_type, new_user_id, uservo, uaccess, clientMgr.Result_key, areaType, holdtime, relaunch_duration, rediswrap.GetRedisCli(), wg, controller)

	}
	return clientMgr
}

func (clientMgr *AreaClientMgr) instantPressure(request_rate int) {
	requestInterval := time.Second / time.Duration(request_rate)
	//创建定时器，n=request_rate个请求打散到1s里面平均请求
	tickCh := time.NewTicker(requestInterval)
	var err_msg string
	ctx := context.Background()
	for _, client := range clientMgr.AreaClients {
		value, _ := ctx_map.Load(client.ClientController.TaskID)
		controller, _ := value.(*routine_controller)
		ctx_routine := controller.ctx

		errCh := make(chan error)
		select {
		case <-tickCh.C:
			go func() {
				defer func() {
					if err := recover(); err != nil {
						vlog.Errorf("panic:%v", err)
					}
				}()
				client.Start(errCh)
				if err, ok := <-errCh; ok {
					if err != nil {
						vlog.Errorf("[aclient] 用户[%s] [%s] client failed, err(%v)，client.status（%d）", client.UserID, client.FlowID, client.fail_msg, client.status)
						if client.fail_msg == nil {
							err_msg = "No Error"
						} else {
							err_msg = client.fail_msg.Error()
						}
						AreaClientInfo := CreateAreaClientInfo(client.Uid, client.Gid, client.bizType, client.AreaType, client.DevType, client.status, 1, client.Atoken, client.Utoken, client.DevID, client.FlowID, client.AreaAddr, client.UServoAddr, client.UAccessAddr, client.UserID, err_msg, client.AllocTime.Sub(client.PreAllocTime).Seconds())

						aci, err := json.Marshal(AreaClientInfo)
						if err != nil {
							fmt.Println("Error marshaling JSON:", err)

						}
						vlog.Infof("[aclient] [%d] [%s]AreaClientInfo(%v)", client.Uid, client.FlowID, AreaClientInfo)

						if _, err := client.redisCli.HSet(ctx, client.Result_key, client.UserID, aci).Result(); err != nil {
							vlog.Infof("save redis fail:(%v)", err)

						}
						return

					}

				}

			}()
		case <-ctx_routine.Done():
			client.wg.Done()
			vlog.Infof("[aclient] 用户[%s] [%s]强制退出！！！！！", client.UserID, client.FlowID)
			return

		}

	}

}

func (clientMgr *AreaClientMgr) normalPressure() {
	var err_msg string
	ctx := context.Background()

	for _, client := range clientMgr.AreaClients {

		errCh := make(chan error)
		client := client
		go func() {
			client.Start(errCh)
			if err, ok := <-errCh; ok {
				if err != nil {
					vlog.Errorf("[aclient] 用户[%s] [%s] start failed, err(%v)，client.status（%d）", client.UserID, client.FlowID, client.fail_msg, client.status)
					if client.fail_msg == nil {
						err_msg = "No Error"
					} else {
						err_msg = client.fail_msg.Error()
					}
					AreaClientInfo := CreateAreaClientInfo(client.Uid, client.Gid, client.bizType, client.AreaType, client.DevType, client.status, 1, client.Atoken, client.Utoken, client.DevID, client.FlowID, client.AreaAddr, client.UServoAddr, client.UAccessAddr, client.UserID, err_msg, client.AllocTime.Sub(client.PreAllocTime).Seconds())

					aci, err := json.Marshal(AreaClientInfo)
					if err != nil {
						vlog.Errorf("json marshal AreaClientInfo error(%v)", err)

					}
					vlog.Infof("[aclient] [%d] [%s]AreaClientInfo(%v)", client.Uid, client.FlowID, AreaClientInfo)
					if _, err := client.redisCli.HSet(ctx, client.Result_key, client.UserID, aci).Result(); err != nil {
						vlog.Errorf("save redis fail:(%v)", err)
					}

				}

			}

		}()

	}
}

func CreateAreaClientInfo(uid, Gid uint64, biz_type uint64, AreaType, DevType, status, is_done int, Atoken, Utoken, DevID, FlowID, AreaAddr, UServoAddr, UAccessAddr, UserID, fail_msg string, alloc_time float64) *AreaClientInfo {

	return &AreaClientInfo{
		Uid:         uid,
		bizType:     biz_type,
		AreaType:    AreaType,
		Atoken:      Atoken,
		Utoken:      Utoken,
		DevID:       DevID,
		Gid:         Gid,
		FlowID:      FlowID,
		AreaAddr:    AreaAddr,
		UServoAddr:  UServoAddr,
		UAccessAddr: UAccessAddr,
		UserID:      UserID,
		DevType:     DevType,
		Status:      status,
		Fail_msg:    fail_msg,
		Is_done:     is_done,
		AllocTime:   alloc_time,
	}

}

func CreateAreaClient(uid, gid uint64, biz_type uint64, userid, uservo, uaccess, result_key string, areaType, holdtime, relaunch_duration int, redisCli redis.UniversalClient, wg *sync.WaitGroup, controller *ClientController) *AreaClient {
	return &AreaClient{

		Uid:              uid,
		Gid:              gid,
		bizType:          biz_type,
		DevID:            gopublic.GenerateRandonString(12),
		AreaType:         areaType,
		FlowID:           fmt.Sprintf("%d_%s", 0, gopublic.GenerateRandonString(12)),
		DevType:          5,
		UServoAddr:       uservo,
		UAccessAddr:      uaccess,
		UserID:           userid,
		HBSigCh:          make(chan bool),
		StartTime:        time.Now(),
		HoldTime:         holdtime,
		status:           0,
		redisCli:         redisCli,
		Result_key:       result_key,
		RelaunchDuration: relaunch_duration,
		RelaunchTimes:    3,
		wg:               wg,
		ClientController: controller,
	}
}

func (client *AreaClient) Start(ch chan error) {

	client.StartTime = time.Now()
	client.QueueTime = client.StartTime
	client.PreAllocTime = client.StartTime

	value, _ := ctx_map.Load(client.ClientController.TaskID)
	controller, _ := value.(*routine_controller)
	ctx_routine := controller.ctx

	go func() {
		defer func() {
			if err := recover(); err != nil {
				vlog.Errorf("panic:%v", err)
			}
		}()
		if !client.ClientController.IsAutoResist {
			if err := client.applyUserToken(ctx_routine); err != nil {
				vlog.Errorf("[aclient] [%d] [%s] applyutoken failed: %v", client.Uid, client.FlowID, err)
				ch <- err
				client.wg.Done()
				close(ch)
				return
			}
		}

		if err := client.unionApply(ctx_routine); err != nil {
			vlog.Errorf("[aclient] [%d] [%s] applyarea failed: %v", client.Uid, client.FlowID, err)
			ch <- err
			client.wg.Done()
			close(ch)
			return
		}

		client.wsConnect(ctx_routine, ch)
		client.launchGame(ctx_routine)

		//close(ch)

	}()

	return
}
